package dao

import (
	"context"
	"gorm.io/gorm"
	"gorm.io/gorm/clause"
	"inventory/api/inventory"
	"strconv"
	"time"

	"inventory/internal/model"
)

// DeleteStorageByStorageKey 删除实时库存
func (d *Dao) DeleteStorageByStorageKey(ctx context.Context, storageKey string) (err error) {
	return d.TransContext(ctx).Delete(&model.Storage{}, "storage_key = ?", storageKey).Error
}

// CreateOrCoverStorage 新增或者覆盖库存
func (d *Dao) CreateOrCoverStorage(ctx context.Context, storage *model.Storage) (err error) {
	err = d.TransContext(ctx).Clauses(clause.OnConflict{
		Columns: []clause.Column{{Name: "warehouse_id"}, {Name: "storage_key"}},
		DoUpdates: clause.Assignments(map[string]interface{}{
			"quantity":    storage.Quantity,
			"update_time": storage.UpdateTime}),
	}).Create(&storage).Error
	if err != nil {
		err = d.processSqlError(err)
	}
	return
}

// CreateOrIncreaseStorage 增量更新库存
func (d *Dao) CreateOrIncreaseStorage(ctx context.Context, storage *model.Storage) (err error) {
	// 增量更新
	err = d.TransContext(ctx).Clauses(clause.OnConflict{
		Columns: []clause.Column{{Name: "warehouse_id"}, {Name: "storage_key"}},
		DoUpdates: clause.Assignments(map[string]interface{}{
			"quantity":    gorm.Expr("inv_storage.quantity + ?", storage.Quantity),
			"update_time": storage.UpdateTime}),
	}).Create(&storage).Error
	if err != nil {
		err = d.processSqlError(err)
	}
	return
}

// DecreaseStorage 库存扣减
func (d *Dao) DecreaseStorage(ctx context.Context, warehouseID uint32, storageKey string, refundQuantity float64,
	updateTime time.Time) (affected bool, err error) {
	// 更新减少量(采购退货在采购时已存在；损耗模式无需确保库存记录一定存在)
	db := d.TransContext(ctx).Model(&model.Storage{
		WarehouseID: warehouseID,
		StorageKey:  storageKey,
	}).Updates(map[string]interface{}{
		"quantity":    gorm.Expr("inv_storage.quantity - ?", refundQuantity),
		"update_time": updateTime,
	})
	err = db.Error
	if err == nil && db.RowsAffected > 0 {
		affected = true
	}
	err = d.processSqlError(err)
	return
}

// FreezeStorage 冻结模式，增加冻结数量
func (d *Dao) FreezeStorage(ctx context.Context, warehouseID uint32, storageKey string, FreezeQuantity float64,
	updateTime time.Time) (affected bool, err error) {
	db := d.TransContext(ctx).Model(&model.Storage{
		WarehouseID: warehouseID,
		StorageKey:  storageKey,
	}).Updates(map[string]interface{}{
		"freeze_quantity": gorm.Expr("inv_storage.freeze_quantity + ?", FreezeQuantity),
		"update_time":     updateTime,
	})
	err = db.Error
	if err == nil && db.RowsAffected > 0 {
		affected = true
	}
	err = d.processSqlError(err)
	return
}

// UnfreezeStorage 解冻模式，仅减少冻结数量
func (d *Dao) UnfreezeStorage(ctx context.Context, warehouseID uint32, storageKey string, unfreezeQuantity float64,
	updateTime time.Time) (affected bool, err error) {
	db := d.TransContext(ctx).Model(&model.Storage{
		WarehouseID: warehouseID,
		StorageKey:  storageKey,
	}).Updates(map[string]interface{}{
		"freeze_quantity": gorm.Expr("case when inv_storage.freeze_quantity - ? >= 0 then inv_storage.freeze_quantity - ? else 0 end",
			unfreezeQuantity, unfreezeQuantity),
		"update_time": updateTime,
	})
	err = db.Error
	if err == nil && db.RowsAffected > 0 {
		affected = true
	}
	err = d.processSqlError(err)
	return
}

// DecreaseAndUnfreezeStorage 发货模式，冻结数量与实际数量一起减少
func (d *Dao) DecreaseAndUnfreezeStorage(ctx context.Context, warehouseID uint32, storageKey string, consumeQuantity float64,
	updateTime time.Time) (affected bool, err error) {
	db := d.TransContext(ctx).Model(&model.Storage{
		WarehouseID: warehouseID,
		StorageKey:  storageKey,
	}).Updates(map[string]interface{}{
		"quantity": gorm.Expr("inv_storage.quantity - ?", consumeQuantity),
		"freeze_quantity": gorm.Expr("case when inv_storage.freeze_quantity - ? >= 0 then inv_storage.freeze_quantity - ? else 0 end",
			consumeQuantity, consumeQuantity),
		"update_time": updateTime,
	})
	err = db.Error
	if err == nil && db.RowsAffected > 0 {
		affected = true
	}
	err = d.processSqlError(err)
	return
}

// FindStorages 获取实时库存
func (d *Dao) FindStorages(ctx context.Context, warehouseID uint32) (storages []*model.Storage, err error) {
	err = d.TransContext(ctx).Model(&model.Storage{}).Where("warehouse_id = ?", warehouseID).Find(&storages).Error
	return
}

// FindStorages 获取实时库存
func (d *Dao) FindStoragesBackup(ctx context.Context, warehouseID uint32) (storages []*model.StorageBak, err error) {
	err = d.TransContext(ctx).Model(&model.StorageBak{}).Where("warehouse_id = ?", warehouseID).Find(&storages).Error
	return
}

// FindStoragesByStorageKeys 获取实时库存
func (d *Dao) FindStoragesByStorageKeys(ctx context.Context, storageKeys []string) (storages []*model.Storage, err error) {
	err = d.TransContext(ctx).Model(&model.Storage{}).Where("storage_key in ?", storageKeys).Find(&storages).Error
	return
}

// FindStoragesByProductIDs 获取指定商品列表实时库存
func (d *Dao) FindStoragesByProductIDs(ctx context.Context, ProductIDs []uint64) (storages []*model.Storage, err error) {
	err = d.TransContext(ctx).Model(&model.Storage{}).Where("prod_id in ?", ProductIDs).Find(&storages).Error
	return
}

// FindWarehouseStoragesByProductIDs 获取指定仓库指定商品列表实时库存
func (d *Dao) FindWarehouseStoragesByProductIDs(ctx context.Context, warehouseID uint32, ProductIDs []uint64) (storages []*model.Storage, err error) {
	err = d.TransContext(ctx).Model(&model.Storage{}).Where("prod_id in ? and warehouse_id = ?",
		ProductIDs, warehouseID).Find(&storages).Error
	return
}

// FindStoragesByMaterialIDs 获取指定物料实时库存
func (d *Dao) FindStoragesByMaterialIDs(ctx context.Context, materialIds []uint64) (storages []*model.Storage, err error) {
	err = d.TransContext(ctx).Model(&model.Storage{}).Where("material_id in ?", materialIds).Find(&storages).Error
	return
}

// FindWarehouseStoragesByStorageKeys 获取指定仓库实时库存
func (d *Dao) FindWarehouseStoragesByStorageKeys(ctx context.Context, warehouseID uint32, storageKeys []string) (
	storages []*model.Storage, err error) {
	err = d.TransContext(ctx).Model(&model.Storage{}).Where(
		"warehouse_id = ? and storage_key in ?", warehouseID, storageKeys).Find(&storages).Error
	return
}

// FindWarehouseStorage 查询实时库存
func (d *Dao) FindWarehouseStorage(ctx context.Context, sgID, warehouseID uint32, pageNo, pageSize,
	queryType int, queryTypeID uint32, queryKeyword string, storageKeys []string) (
	total int64, storages []*model.Storage, err error) {
	db := d.TransContext(ctx).Model(&model.Storage{}).Where("warehouse_id = ? ", warehouseID)
	if queryTypeID != 0 {
		db = db.Where("remark->>'type_id' = ? ", strconv.Itoa(int(queryTypeID)))
	}
	if queryType > 0 {
		db = db.Where("storage_type = ?", queryType)
	}
	if queryKeyword != "" {
		db = db.Where("(remark->>'code' like ?", "%"+queryKeyword+"%")
		if queryType != int(inventory.StorageType_PROD) {
			var materialIDs []int
			mdb := d.TransContext(ctx).Model(&model.Material{}).Where(
				"sg_id = ? AND name LIKE ?", sgID, "%"+queryKeyword+"%")
			if queryTypeID != 0 {
				mdb = mdb.Where("mt_id = ?", queryTypeID)
			}
			err = mdb.Pluck("id", &materialIDs).Error
			if err != nil {
				return
			}
			if len(materialIDs) > 0 {
				db = db.Or("material_id IN ?", materialIDs)
			}
		}
		if queryType != int(inventory.StorageType_MATERIAL) {
			db = db.Or("remark->>'name' like ? ", "%"+queryKeyword+"%")
		}
		db = db.Where("true )")
	}

	if storageKeys != nil {
		db = db.Where("storage_key IN ?", storageKeys)
	}
	if err = db.Count(&total).Error; err != nil {
		return
	}
	err = db.Order("storage_key asc").Offset((pageNo - 1) * pageSize).Limit(pageSize).Find(&storages).Error
	return
}
